/**
  ******************************************************************************
  * @file    oled.c 
  * @author  TONLY
  * @version V1.0
  * @date    2022-01-01
  * @brief   oled的操作
  ******************************************************************************
  * @attention
  * 
  *	本程序只供学习使用，未经作者许可，不得用于其它任何用途
  *	版权所有，盗版必究
  *
  * <h2><center>&copy; COPYRIGHT 2022 华清-TONLY </center></h2>
  ******************************************************************************
  */  

/* Includes ------------------------------------------------------------------*/
#include "oled.h"
#include "font.h"
#include "delay.h"


/* Private define ------------------------------------------------------------*/
	/* OLED_CLK */
#define OLED_CLK_RCC		RCC_APB2Periph_GPIOC
#define OLED_CLK_GPIO		GPIOC
#define OLED_CLK_MODE		GPIO_Mode_Out_PP
#define OLED_CLK_SPEED	GPIO_Speed_50MHz
#define OLED_CLK_PIN		GPIO_Pin_4
#define OLED_CLK			  PCout(4)

	/* OLED_SDA */
#define OLED_SDA_RCC		RCC_APB2Periph_GPIOC
#define OLED_SDA_GPIO		GPIOC
#define OLED_SDA_MODE		GPIO_Mode_Out_PP
#define OLED_SDA_SPEED	GPIO_Speed_50MHz
#define OLED_SDA_PIN		GPIO_Pin_5
#define OLED_SDA			  PCout(5)
	
	/* OLED_RES */
#define OLED_RES_RCC		RCC_APB2Periph_GPIOB
#define OLED_RES_GPIO		GPIOB
#define OLED_RES_MODE		GPIO_Mode_Out_PP
#define OLED_RES_SPEED	GPIO_Speed_50MHz
#define OLED_RES_PIN		GPIO_Pin_0
#define OLED_RES			  PBout(0)

	/* OLED_DC */
#define OLED_DC_RCC			RCC_APB2Periph_GPIOB
#define OLED_DC_GPIO		GPIOB
#define OLED_DC_MODE		GPIO_Mode_Out_PP
#define OLED_DC_SPEED		GPIO_Speed_50MHz
#define OLED_DC_PIN			GPIO_Pin_1
#define OLED_DC				  PBout(1)

	/* OLED_CS */
//#define OLED_CS_RCC			RCC_APB2Periph_GPIOA
//#define OLED_CS_GPIO		GPIOA
//#define OLED_CS_MODE		GPIO_Mode_Out_PP
//#define OLED_CS_SPEED		GPIO_Speed_50MHz
//#define OLED_CS_PIN			GPIO_Pin_8
//#define OLED_CS				  PAout(8)

/* OLED 为 128*64 */
#define WIDTH				  128			//宽
#define HEIGHT				64			//高
#define PAGE_SIZE			(HEIGHT/8)	//页

#define Brightness 			0x00   		//清屏后 0x00黑底   0xFF白底

#define XLEVEL_L			0x02		//设置显示时的起始列地址低四位（取低四位）
#define XLEVEL_H			0x10		//设置显示时的起始列地址高四位（取低四位）
#define YLEVEL				0xB0		//设置页地址，其低三位的值对应着 GRAM 的页地址

/* Private variables ---------------------------------------------------------*/		   
static u8 OLEDGram[WIDTH][PAGE_SIZE];//显存

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  m的n次方
  * @param  m ：底数
  * @param  n ：指数
  * @retval m的n次方值
  */
static u32 mypow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}


/**
  * @brief  OLED写入一个字节
  * @param 	data ：需要写入的数据
  * @param 	cmd  ：写入的是数据还是命令
  *				@arg OLED_DAT 数据  1
  *				@arg OLED_CMD 命令  0
  * @retval None
  */
static void OLED_WR_Byte(u8 data,u8 cmd)
{	
	u8 i;
	if(cmd)						//输入的数据还是命令
		OLED_DC = 1;
	else 
		OLED_DC = 0;
	//OLED_CS = 0;				//片选信号拉低
	for(i=0;i<8;i++){			//将data写入屏幕
		OLED_CLK = 0;			//拉低时钟线
		if(data&0x80)			//判断data最高位
			OLED_SDA = 1;
		else 
			OLED_SDA = 0;
		OLED_CLK = 1;			//拉高，上升沿读取数据
		data<<=1;
	}
	//OLED_CS = 1;				//片选信号拉高
	OLED_DC = 1;				
} 


/**
  * @brief  画点
  * @param  x ：指定位置横坐标（0~127） WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）  HEIGHT：64	
  * @param  mode ：0清空  1填充
  * @retval None
  */
static void OLED_DrawPoint(u8 x,u8 y,u8 mode)
{
	u8 pos,data;
	if(x>=WIDTH || y>=HEIGHT )
		return;			//超出范围
	pos = y/8;
	data = 1<<(y%8);
	
	if(mode)
		OLEDGram[x][pos]|=data;
	else
		OLEDGram[x][pos]&=~data;
}


/**
  * @brief  读点
  * @param  x ：指定位置横坐标（0~127） WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）  HEIGHT：64	
  * @retval u8 读取的值
  */
/*
static u8 OLED_ReadPoint(u8 x,u8 y)
{
	u8 pos,temp=0x00;
	//x = 127-x;
	y = HEIGHT-1-y;
	pos=y/8;
	temp=1<<(y%8);
	if(temp&OLEDGram[pos][x]) 
		return 1;
	return 0;
}*/


/**
  * @brief  OLED定位
  * @param  x ：宽度的坐标  
  * @param  y ：页的坐标 0 1 2 3 4 5 6 7
  * @retval None
  */
static void OLED_SetPos(u8 x,u8 y)
{
	OLED_WR_Byte(YLEVEL+y,OLED_CMD);
	OLED_WR_Byte((x&0x0f)|XLEVEL_L,OLED_CMD);
	OLED_WR_Byte((x&0xf0)>>4|XLEVEL_H,OLED_CMD);
}


/* Public functions ----------------------------------------------------------*/

/**
  * @brief	oled初始化
  * @param 	None
  *	@retval None
  */
void OLED_Init(void)
{
	static u8 isOLEDInit = 0;
	GPIO_InitTypeDef GPIO_InitStruct;
	
	if(isOLEDInit)
		return;
	
	delay_init();
	
	//开时钟
	RCC_APB2PeriphClockCmd(OLED_CLK_RCC|OLED_SDA_RCC|OLED_RES_RCC|OLED_DC_RCC/*|OLED_CS_RCC*/,ENABLE);
	
	//OLED_CLK  
	GPIO_InitStruct.GPIO_Mode 		= OLED_CLK_MODE;
	GPIO_InitStruct.GPIO_Pin 		  = OLED_CLK_PIN;
	GPIO_InitStruct.GPIO_Speed 		= OLED_CLK_SPEED;
	GPIO_Init(OLED_CLK_GPIO,&GPIO_InitStruct);
	
	//OLED_SDA
	GPIO_InitStruct.GPIO_Mode 		= OLED_SDA_MODE;
	GPIO_InitStruct.GPIO_Pin 		  = OLED_SDA_PIN;
	GPIO_InitStruct.GPIO_Speed 		= OLED_SDA_SPEED;
	GPIO_Init(OLED_SDA_GPIO,&GPIO_InitStruct);
	
	//OLED_RES
	GPIO_InitStruct.GPIO_Mode 		= OLED_RES_MODE;
	GPIO_InitStruct.GPIO_Pin 		  = OLED_RES_PIN;
	GPIO_InitStruct.GPIO_Speed 		= OLED_RES_SPEED;
	GPIO_Init(OLED_RES_GPIO,&GPIO_InitStruct);
	
	//OLED_DC
	GPIO_InitStruct.GPIO_Mode 		= OLED_DC_MODE;
	GPIO_InitStruct.GPIO_Pin 		  = OLED_DC_PIN;
	GPIO_InitStruct.GPIO_Speed 		= OLED_DC_SPEED;
	GPIO_Init(OLED_DC_GPIO,&GPIO_InitStruct);
	
	//OLED_CS
	/*
	GPIO_InitStruct.GPIO_Mode 		= OLED_CS_MODE;
	GPIO_InitStruct.GPIO_Pin 		= OLED_CS_PIN;
	GPIO_InitStruct.GPIO_Speed 		= OLED_CS_SPEED;
	GPIO_Init(OLED_CS_GPIO,&GPIO_InitStruct);
	*/
	/* 全部拉高 */
	OLED_CLK = 1;
	OLED_SDA = 1;
	OLED_RES = 1;
	OLED_DC = 1;
	//OLED_CS = 1;
	
	/* 复位 */
	delay_ms(100);
	OLED_RES = 0;
	delay_ms(200);
	OLED_RES = 1;
#if 1	
	/* oled配置 */
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0x7F,OLED_CMD);// Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
#else
  OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/ 
	OLED_WR_Byte(0x02,OLED_CMD); /*set lower column address*/ 
	OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/ 
	OLED_WR_Byte(0x40,OLED_CMD); /*set display start line*/ 
	OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
	OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/ 
	OLED_WR_Byte(0xcf,OLED_CMD); /*128*/ 
	OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/ 
	OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/ 
	OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/ 
	OLED_WR_Byte(0x3F,OLED_CMD); /*duty = 1/64*/ 
	OLED_WR_Byte(0xad,OLED_CMD); /*set charge pump enable*/ 
	OLED_WR_Byte(0x8b,OLED_CMD); /* 0x8B 内供 VCC */ 
	OLED_WR_Byte(0x33,OLED_CMD); /*0X30---0X33 set VPP 9V */ 
	OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/ 
	OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/ 
	OLED_WR_Byte(0x00,OLED_CMD); /* 0x20 */ 
	OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/ 
	OLED_WR_Byte(0x80,OLED_CMD); 
	OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/ 
	OLED_WR_Byte(0x1f,OLED_CMD); /*0x22*/ 
	OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/ 
	OLED_WR_Byte(0x12,OLED_CMD); 
	OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/ 
	OLED_WR_Byte(0x40,OLED_CMD);
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
#endif
	/* 清屏 */
	OLED_Clear();
	OLED_SetPos(0,0);
	
	isOLEDInit = 1;
}


/**
  * @brief  OLED清屏
  * @note   清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
  * @param  None
  * @retval None
  */
void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<WIDTH;i++)
		for(n=0;n<PAGE_SIZE;n++)
			OLEDGram[i][n] = Brightness;
	OLED_RefreshGram();			//更新
} 


/**
  * @brief  OLED更新显存
  * @param None
  * @retval None
  */
void OLED_RefreshGram(void)
{	
	u8 i,n;
	
	for(i=0;i<PAGE_SIZE;i++){
		OLED_SetPos(0,i);
		for(n=0;n<WIDTH;n++){
			OLED_WR_Byte(OLEDGram[n][i],OLED_DAT);
		}
	}
} 


/**
  * @brief  开启OLED显示
  * @param  None
  * @retval None
  */
void OLED_DisplayON(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}


/**
  * @brief  关闭OLED显示
  * @param  None
  * @retval None
  */     
void OLED_DisplayOFF(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}	


/**
  * @brief  填充
  * @param  x1,y1,x2,y2 填充区域的对角坐标
  * @param  dot ：0 清空  1填充
  * @retval None
  */
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)  
{  
	u8 x,y;  
	for(x=x1;x<=x2;x++)
		for(y=y1;y<=y2;y++)
			OLED_DrawPoint(x,y,dot);											    
//	oled_refreshGram();//更新显示
}

/**
  * @brief  指定位置显示字符
  * @param  x ：指定位置横坐标（0~127）	WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）	HEIGHT：64
  * @param  chr ：字符
  * @param  f_w ：字宽  
  * @param  f_h ：字高 
  * @note   f_w : f_h  ==>  6*8   6*12  12*24   
  * @param  mode ：0反白显示  1正常显示
  * @retval None
  */
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 f_w,u8 f_h,u8 mode)
{
	u8 i,n,temp;
	u8 y0 = y;
	u8 csize = f_w*(f_h/8+(f_h%8?1:0)); //字节数
	chr = chr-' ';  //偏移量
	
	for(i=0;i<csize;i++){
		if(f_w==6&&f_h==8)
			temp = asc2_0608[chr][i];//调用0608ascii字体
		else if(f_w==6&&f_h==12)
			temp = asc2_0612[chr][i];//调用0612ascii字体
		else if(f_w==12&&f_h==24)
			temp = asc2_1224[chr][i];//调用1224ascii字体
		else 
			return;// 没有词库

		for(n=0;n<8;n++){
			if(temp&0x80)
				OLED_DrawPoint(x,y,mode);
			else
				OLED_DrawPoint(x,y,!mode);
			
			temp<<=1;
			y++;
			if(y-y0==f_h){
				y = y0;
				x++;
				break;
			}
		}
	}
}

/**
  * @brief  指定位置显示数字
  * @param  x ：指定位置横坐标（0~127）	WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）	HEIGHT：64
  * @param  num ：数字 （0~4294967295）
  * @param  len ：数字的位数
  * @param  f_w ：字宽  
  * @param  f_h ：字高 
  * @note 	f_w : f_h  ==>  6*8   6*12  12*24   
  * @param  mode ：0反白显示  1正常显示
  * @retval None
  */
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 f_w,u8 f_h,u8 mode)
{
	u8 i,temp;
	u8 enshow=0;		
	for(i=0;i<len;i++){
		temp=(num/mypow(10,len-i-1))%10;	//得到每一个位
		if(enshow==0&&i<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(f_w)*i,y,' ',f_w,f_h,mode);
				continue;
			}
			else 
				enshow=1; 
		}
		OLED_ShowChar(x+(f_w)*i,y,temp+'0',f_w,f_h,mode);
	}
}


/**
  * @brief  指定位置显示数字
  * @param  x ：指定位置横坐标（0~127）	WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）	HEIGHT：64
  * @param  p ：字符串的首地址
  * @param  f_w ：字宽  
  * @param  f_h ：字高 
  * @note   f_w : f_h  ==>  6*8   6*12  12*24   
  * @param  mode ：0反白显示  1正常显示
  * @retval None
  */
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 f_w,u8 f_h,u8 mode)
{	
    while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
    {       
        if(x>(WIDTH-(f_w))){   //换行
			x=0;
			y+=f_h;
		}
		
        if(y>(HEIGHT-f_h)){		//到底清空从头写
			y=x=0;
			OLED_Clear();
		}

//		delay_ms(100);
        OLED_ShowChar(x,y,*p,f_w,f_h,mode);	 
//		OLED_RefreshGram();
        x+=f_w;
        p++;
    }
}


/**
  * @brief  显示图片
  * @param  x ：指定位置横坐标（0~127）	WIDTH ：128
  * @param  y ：指定位置纵坐标（0~63）	HEIGHT：64
  * @param  p ：图片起始地址
  * @param  p_w ：图片宽（单位像素）  
  * @param  p_h ：图片高（单位像素）    
  * @param  mode ：0反白显示  1正常显示
  * @retval None
  */
void OLED_ShowPicture(u8 x,u8 y,const u8 *p,u8 p_w,u8 p_h,u8 mode)
{	
	u8 temp,i,col,row;
	u8 y0=y;
	
	u8 width=p_w;
	if(x+p_w>WIDTH)
		width=WIDTH-p_w;//实际显示宽度
	
	u8 high=p_h;
	if(y+p_h>HEIGHT)
		high=HEIGHT-p_h;//实际显示高度
	
	u8 exp_col_bytes=(p_h/8+((p_h%8)?1:0));		//显示一列的字节数
	u8 act_col_bytes=(high/8+((high%8)?1:0));	//实际显示一列的字节数
	
	for(row=0;row<width;row++)// 列++
	{
		for(col=0;col<act_col_bytes;col++)//显示一列
		{   
			temp = p[col+row*exp_col_bytes];
			for(i=0;i<8;i++)
			{
				if(temp&0x80)
					OLED_DrawPoint(x,y,mode);
				else 
					OLED_DrawPoint(x,y,!mode);
				temp<<=1;
				y++;
				if((y-y0)==high)
				{
					y=y0;
					x++;
					break;
				}		
			} 
		}
	}		
}



/******************* (C) COPYRIGHT 2022 华清-TONLY *************END OF FILE****/
